package com.aura.springbook.common.util;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;

/**
 * 自定义密码加密工具类
 */
public class PasswordUtil {

    private static final int ITERATIONS = 10000;
    private static final int KEY_LENGTH = 256;
    private static final String ALGORITHM = "PBKDF2WithHmacSHA256";

    /**
     * 加密密码
     * <p>
     * 使用 PBKDF2算法对明文密码进行加密，生成包含随机盐值的加密字符串
     *
     * @param password 明文密码
     * @return 加密后的密码（格式：盐值:哈希值，均为Base64编码）
     */
    public static String encrypt(String password) {
        try {
            // 生成随机盐值
            SecureRandom random = new SecureRandom();
            byte[] salt = new byte[16];
            random.nextBytes(salt);

            // 使用PBKDF2算法加密
            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
            SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
            byte[] hash = factory.generateSecret(spec).getEncoded();

            // 将盐值和哈希值组合后进行Base64编码
            String saltBase64 = Base64.getEncoder().encodeToString(salt);
            String hashBase64 = Base64.getEncoder().encodeToString(hash);

            return saltBase64 + ":" + hashBase64;
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new RuntimeException("密码加密失败", e);
        }
    }

    /**
     * 验证密码
     * <p>
     * 将明文密码使用存储的盐值进行相同加密，然后与存储的哈希值进行安全比较
     *
     * @param password       明文密码
     * @param hashedPassword 加密后的密码（格式：盐值:哈希值）
     * @return 是否匹配
     */
    public static boolean verify(String password, String hashedPassword) {
        try {
            // 分离盐值和哈希值
            String[] parts = hashedPassword.split(":");
            if (parts.length != 2) {
                return false;
            }

            byte[] salt = Base64.getDecoder().decode(parts[0]);
            byte[] hash = Base64.getDecoder().decode(parts[1]);

            // 使用相同的参数对输入密码进行加密
            PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
            SecretKeyFactory factory = SecretKeyFactory.getInstance(ALGORITHM);
            byte[] testHash = factory.generateSecret(spec).getEncoded();

            // 比较哈希值
            return slowEquals(hash, testHash);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            return false;
        }
    }

    /**
     * 安全地比较两个字节数组是否相等（防止时序攻击）
     * <p>
     * 使用异或操作进行比较，确保比较时间不泄露信息
     *
     * @param a 第一个数组
     * @param b 第二个数组
     * @return 是否相等
     */
    private static boolean slowEquals(byte[] a, byte[] b) {
        int diff = a.length ^ b.length;
        for (int i = 0; i < a.length && i < b.length; i++) {
            diff |= a[i] ^ b[i];
        }
        return diff == 0;
    }
}